/*******************************************************************************************
*文件:    S7comm.cpp
*描述:    S7协议分析
*
*作者:    刘涛
*日期:    2017-03-30
*修改:    创建文件                            ------>     2017-03-28
*         修改RFC标准处理                     ------>     2017-03-29
*         修改程序结构，进行代码分层处理      ------>     2017-05-05
*         增加TPKT和COTP检查                  ------>     2017-05-11
*         张冬波修改, 增加读写控制            ------>     2017-10-05
*
*******************************************************************************************/
#include "datatype.h"
#include "stringex.h"
#include "debugout.h"
#include "../protocols.h"
#include "../rules.h"
#include "s7protocol.h"
#include "s7comm.h"

const pchar CS7COMM::MODULE = "SUS7COMMLIB";
const pchar CS7COMM::VERSION = "1.0.1_"__DATE__;
const pchar CS7COMM::REGKEY = "SUS7COMMLIB_1.0.1";

#define S7COMM_PROTOCOL_ID   (0x32)    //S7comm protocol_id 
#define S7COMM_COTP_CC       (0xd0)    //s7comm cotp connect confirm
#define S7COMM_COTP_CR       (0xe0)    //s7comm cotp connect request
#define S7COMM_COTP_DT       (0xf0)    //s7comm cotp data


#define COTP_MIN_SIZE (3) //cotp len
#define COTP_REQUEST_SIZE (18) //cotp len for request size
#define COTP_CONFIRM_SIZE (18) //cotp len for response size
#define TPKT_HEADER_SIZE (4) //TPKT len

//S7 Func map类型
#define S7COMM_FUNC_TYPE_MAP (1)
#define S7COMM_DATA_TYPE_MAP (2)
#define S7COMM_FUNC_GRP_MAP  (3)
#define S7COMM_SUB_FUNC_MAP  (4)
#define S7COMM_PDU_TYPE_MAP  (5)

//S7 rule map类型
#define S7COMM_FUNC_RULE_MAP        (1)
#define S7COMM_TYPE_RULE_MAP        (2)
#define S7COMM_FUNCGRP_RULE_MAP     (3)
#define S7COMM_SUBFUNC_RULE_MAP     (4)

#define ACCEPT_FILTER(filter) (((filter) == RULE_BLACK) ? PROTO_ACCEPT : PROTO_DROP)
#define DROP_FILTER(filter)   (((filter) == RULE_BLACK) ? PROTO_DROP : PROTO_ACCEPT)

#define GET_MAP_LEN(map)      (sizeof(map) / sizeof(map[0]))

#define UD_FUNCGRP_FLAG (0)
#define UD_TYPE_FLAG    (1)
#define GET_UD_TYPE(data,flag) ( (UD_TYPE_FLAG == (flag))?(((data) >> 4) & 0xf):((data) & 0xf) )

//TPKT state
typedef enum {
    TPKT_PACKET_COMPLETE = 0,
    TPKT_WAITING = 1,
    TPKT_ERROR = 2
} TpktState;

//func map struct
typedef struct _S7commfuncmap {
    uint8 func;
    pchar name;
    int32 rw;
} S7COMMFUNCMAP, *PS7COMMFUNCMAP;

//subfunc map struct
typedef struct _S7commsubfuncmap {
    uint8 funcgrp;
    uint8 subfunc;
    pchar funcgrpname;
    pchar subfuncname;
    int32 rw;
} S7COMMSUBFUNCMAP, *PS7COMMSUBFUNCMAP;

// Function codes in parameter part
const S7COMMFUNCMAP S7CommFuncMap[] = {
    { S7COMM_SERV_CPU,             "CPU_services"       , PROTO_RWNULL},
    { S7COMM_SERV_SETUPCOMM,       "Setup_communication", PROTO_WRITE},
    { S7COMM_SERV_READVAR,         "Read_Var"           , PROTO_READ},
    { S7COMM_SERV_WRITEVAR,        "Write_Var"          , PROTO_WRITE},
    { S7COMM_FUNC_REQUESTDOWNLOAD, "Request_download"   , PROTO_WRITE},
    { S7COMM_FUNC_DOWNLOADBLOCK,   "Download_block"     , PROTO_WRITE},
    { S7COMM_FUNC_DOWNLOADENDED,   "Download_ended"     , PROTO_WRITE},
    { S7COMM_FUNC_STARTUPLOAD,     "Start_upload"       , PROTO_RWNULL},
    { S7COMM_FUNC_UPLOAD,          "Upload"             , PROTO_RWNULL},
    { S7COMM_FUNC_ENDUPLOAD,       "End_upload"         , PROTO_RWNULL},
    { S7COMM_FUNC_PISERVICE,       "PI_Service"         , PROTO_RWNULL},
    { S7COMM_FUNC_PLCSTOP,         "PLC_Stop"           , PROTO_WRITE}
};

// Function codes in parameter part
const S7COMMFUNCMAP S7CommDataMap[] = {
    { PUSH_TYPE,     "Push"    , PROTO_RWNULL},
    { REQUEST_TYPE,  "Request" , PROTO_RWNULL},
    { RESPONSE_TYPE, "Response", PROTO_RWNULL}
};

//Names of Function groups in userdata parameter part
const S7COMMFUNCMAP S7CommFuncGrpMap[] = {
    { S7COMM_UD_FUNCGROUP_MODETRANS,        "Mode_transition"    , PROTO_RWNULL},
    { S7COMM_UD_FUNCGROUP_PROG,             "Programmer_commands", PROTO_RWNULL},
    { S7COMM_UD_FUNCGROUP_CYCLIC,           "Cyclic_data"        , PROTO_READ},     // to read data from plc without a request
    { S7COMM_UD_FUNCGROUP_BLOCK,            "Block_functions"    , PROTO_RWNULL},
    { S7COMM_UD_FUNCGROUP_CPU,              "CPU_functions"      , PROTO_RWNULL},
    { S7COMM_UD_FUNCGROUP_SEC,              "Security"           , PROTO_RWNULL},
    { S7COMM_UD_FUNCGROUP_PBC,              "PBC_BSEND/BRECV"    , PROTO_RW},
    { S7COMM_UD_FUNCGROUP_TIME,             "Time_functions"     , PROTO_RWNULL},
    { S7COMM_UD_FUNCGROUP_NCPRG,            "NC_programming"     , PROTO_WRITE},
};

//sub Function part
const S7COMMSUBFUNCMAP S7CommSubFuncMap[] = {
    { S7COMM_UD_FUNCGROUP_PROG,   PC_VARTAB,           "Programmer_commands", "VarTab"             , PROTO_RWNULL},
    { S7COMM_UD_FUNCGROUP_PROG,   PC_READDIAGDATA,     "Programmer_commands", "Read_diag_data"     , PROTO_READ},
    { S7COMM_UD_FUNCGROUP_BLOCK,  BF_LISTBLOCKS,       "Block_functions",     "List_blocks"        , PROTO_READ},
    { S7COMM_UD_FUNCGROUP_BLOCK,  BF_LISTBLOCKSOfTYPE, "Block_functions",     "List_blocks_of_type", PROTO_READ},
    { S7COMM_UD_FUNCGROUP_BLOCK,  BF_GETBLOCKINFO,     "Block_functions",     "Get_block_info"     , PROTO_READ},
    { S7COMM_UD_FUNCGROUP_CPU,    CF_READSZL,          "CPU_functions",       "Read_SZL"           , PROTO_READ},
    { S7COMM_UD_FUNCGROUP_CPU,    CF_SYSTEMSTATE,      "CPU_functions",       "System_state"       , PROTO_READ},
    { S7COMM_UD_FUNCGROUP_TIME,   TF_READCLOCK,        "Time_functions",      "Read_Clock"         , PROTO_READ},
    { S7COMM_UD_FUNCGROUP_TIME,   TF_SETCLOCK,         "Time_functions",      "Set_Clock"          , PROTO_WRITE},
    { S7COMM_UD_FUNCGROUP_CYCLIC, CD_MEMORY,           "Cyclic_data",         "Memory"             , PROTO_READ}
};

// PDU types
const S7COMMFUNCMAP S7CommPduTypeMap[] = {
    { S7COMM_ROSCTR_JOB,                    "JOB"      , PROTO_RWNULL},   //Request: job with acknowledgement
    { S7COMM_ROSCTR_ACK,                    "ACK"      , PROTO_RWNULL},   //acknowledgement without additional field
    { S7COMM_ROSCTR_ACK_DATA,               "ACK Data" , PROTO_RWNULL},   //Response: acknowledgement with additional field
    { S7COMM_ROSCTR_USERDATA,               "User Data", PROTO_RWNULL},
};

/**
 * 构造函数
 */
CS7COMM::CS7COMM()
{
    memset(&rulebak, 0, sizeof(rulebak));
    strcpy(m_debughead, "S7COMM");
}

/**
 * [CS7COMM::initialize 模块注册验证]
 * @param  verdict [验证路径]
 * @return         [PROTO_OK 成功]
 */
int32 CS7COMM::initialize(const pchar verdict)
{
    return _initialize(verdict, CS7COMM::MODULE, CS7COMM::REGKEY);
}

/**
 * [CS7COMM::version 获取模块版本信息]
 * @param  version [description]
 * @return         [PROTO_ERROR失败]
 */
int32 CS7COMM::version(pchar version)
{
    return _version(version, CS7COMM::MODULE, CS7COMM::VERSION);
}

/**
 * [CS7COMM::CheckTpkt 传输层TPKT协议处理：判断数据是否收完,\
 *                     借鉴于库函数CotpConnection_readToTpktBuffer]
 * @param  pkt [数据包]
 * @param  len [数据大小]
 * @return     [如果此帧数据完整收完，则返回TPKT_PACKET_COMPLETE；\
 *                      如果发生了拼包或者拆包，则不解析次包]
 */
uint8 CS7COMM::CheckTpkt(const uint8 *pkt, uint32 len)
{
    uint32 data_len = pkt[2] * 256 + pkt[3]; //大端数据

    PRINT_DBG_HEAD;
    print_dbg("read_pktp data_len = %d,len = %d", data_len, len);


    if (data_len == len) { //收完了所有数据
        return TPKT_PACKET_COMPLETE;
    } else if (data_len > len) { //发生了拼包
        return TPKT_WAITING;
    } else {  //data_len < len:发生了拆包
        return TPKT_ERROR;
    }

}

/**
 * [CS7COMM::GetCotpOffset 传输层COTP协议处理，借鉴于库函数 CotpConnection_parseIncomingMessage]
 * @param  buffer [数据包]
 * @return        [返回的偏移量]
 */
uint32 CS7COMM::GetCotpOffset(const uint8 *buffer)
{
    uint32 offset = buffer[0] + 1;//cotp length + 1

    PRINT_DBG_HEAD;
    print_dbg("get_cotp_offset offset = %d", offset);

    return offset;
}

/**
 * [CS7COMM::GetCotpType 传输层COTP协议处理]
 * @param  buffer [数据包]
 * @return        [返回type值]
 */
uint8 CS7COMM::GetCotpType(const uint8 *buffer)
{
    uint8 type = buffer[1];//cotp 第二个字节

    PRINT_DBG_HEAD;
    print_dbg("get_cotp_type cotp_type = %x", type);

    return type;
}

/**
 * [CS7COMM::CheckTpktCotp 检查TPKT+COTP]
 * @param  data [数据包]
 * @param  size [数据大小]
 * @return     [正值是返回的偏移量]
 */
uint32  CS7COMM::CheckTpktCotp(const uint8 *data, uint32 size)
{

    //TPTK+COTP验证
    if ( (TPKT_HEADER_SIZE + COTP_MIN_SIZE) >= size ) {
        PRINT_DBG_HEAD;
        print_dbg("recv un-complate package, size = %d", size);
        return 0;
    }

    //TPKT检查
    uint8 tpktState = CheckTpkt((uint8 *)data, size);
    if ( TPKT_PACKET_COMPLETE != tpktState ) {
        PRINT_DBG_HEAD;
        print_dbg("recv un-complate tpkt package, tpktstate = %d", tpktState);
        return 0;
    }

    //COTP检查
    uint32 cotp_offset = GetCotpOffset((const uint8 *)data + TPKT_HEADER_SIZE);
    uint8 cotp_type = GetCotpType(data + TPKT_HEADER_SIZE);
    if ( S7COMM_COTP_DT == cotp_type ) {
        if ( COTP_MIN_SIZE != cotp_offset ) {
            PRINT_DBG_HEAD;
            print_dbg("recv un-complate cotp package, cotptype = %d, cotp_offset = %d", cotp_type, cotp_offset);
            return 0;
        }
    } else if((S7COMM_COTP_CR == cotp_type) || (S7COMM_COTP_CC == cotp_type)) {
        if ( COTP_REQUEST_SIZE != cotp_offset) {    
            PRINT_DBG_HEAD;
            print_dbg("recv un-complate cotp package, cotptype = %d, cotp_offset = %d", cotp_type, cotp_offset);
            return 0;
        }
    } else {
        PRINT_DBG_HEAD;
        print_dbg("recv un-complate cotp package, cotptype = %d, cotp_offset = %d", cotp_type, cotp_offset);
        return 0;
    }

    return TPKT_HEADER_SIZE + cotp_offset;
}

/**
 * [CS7COMM::S7UDPduMap S7协议user_data pdu字段验证]
 * @param  src_data [源数据]
 * @param  map_type [匹配类型]
 * @return          [-1匹配失败]
 */
int32 CS7COMM::S7UDPduMap( PS7COMMSTATE src_data, uint8 map_type )
{
    int32 ret = -1;
    uint8 func_grp = 0;
    uint8 ud_type  = 0;
    uint8 group    = 0;

    PRINT_DBG_HEAD;
    print_dbg("S7 map_type = %d", map_type);

    switch (map_type) {
    case S7COMM_DATA_TYPE_MAP:
        ud_type = GET_UD_TYPE(src_data->PARM.UD.type, UD_TYPE_FLAG);
        for (int32 i = 0; i < GET_MAP_LEN(S7CommDataMap); i++ ) {
            if ( ud_type == S7CommDataMap[i].func ) {
                ret = i;
                PRINT_DBG_HEAD;
                print_dbg("S7 data type: %x = [%s]", ud_type, S7CommDataMap[i].name);
                break;
            }
        }
        break;

    case S7COMM_FUNC_GRP_MAP:
        func_grp = GET_UD_TYPE(src_data->PARM.UD.type, UD_FUNCGRP_FLAG);
        for (int32 i = 0; i < GET_MAP_LEN(S7CommFuncGrpMap); i++ ) {
            if ( func_grp == S7CommFuncGrpMap[i].func ) {
                ret = i;
                PRINT_DBG_HEAD;
                print_dbg("S7 fucntion group: %x = [%s]", func_grp, S7CommFuncGrpMap[i].name);
                break;
            }
        }
        break;

    case S7COMM_SUB_FUNC_MAP:
        group = GET_UD_TYPE(src_data->PARM.UD.type, UD_FUNCGRP_FLAG);
        for (int32 i = 0; i < GET_MAP_LEN(S7CommSubFuncMap); i++ ) {
            if ( (group == S7CommSubFuncMap[i].funcgrp) \
                 && (src_data->PARM.UD.sub_func == S7CommSubFuncMap[i].subfunc )) {
                ret = i;
                PRINT_DBG_HEAD;
                print_dbg("S7 subfucntion: %x = [%s]", src_data->PARM.UD.sub_func, \
                          S7CommSubFuncMap[i].subfuncname);
                break;
            }
        }
        break;
    }

    PRINT_DBG_HEAD;
    print_dbg("S7 S7UDPduMap:ret = %d", ret);

    return ret;
}

/**
 * [CS7COMM::S7FuncNameMap S7协议pdu字段验证]
 * @param  value     [待验证值]
 * @param  sub_value [子类型值]
 * @param  map_type  [匹配类型]
 * @return           [-1失败]
 */
int32 CS7COMM::S7FuncNameMap( uint8 value, uint8 sub_value, uint8 map_type )
{
    int32 ret = -1;

    PRINT_DBG_HEAD;
    print_dbg("S7 value = %x,map_type = %d", value, map_type);

    switch ( map_type ) {
    case S7COMM_FUNC_TYPE_MAP:
        for (int32 i = 0; i < GET_MAP_LEN(S7CommFuncMap); i++ ) {//GET  int32 i = 0;
            if ( value == S7CommFuncMap[i].func ) {
                ret = i;
                PRINT_DBG_HEAD;
                print_dbg("S7 pdu function: %x = [%s]", value, S7CommFuncMap[i].name);
                break;
            }
        }
        break;

    case S7COMM_PDU_TYPE_MAP:
        for (int32 i = 0; i < GET_MAP_LEN(S7CommPduTypeMap); i++ ) {
            if ( value == S7CommPduTypeMap[i].func ) {
                ret = i;
                PRINT_DBG_HEAD;
                print_dbg("S7 pdu type: %x = [%s]", value, S7CommPduTypeMap[i].name);
                break;
            }
        }
        break;

    case S7COMM_DATA_TYPE_MAP:
        for (int32 i = 0; i < GET_MAP_LEN(S7CommDataMap); i++ ) {
            if ( value == S7CommDataMap[i].func ) {
                ret = i;
                PRINT_DBG_HEAD;
                print_dbg("S7 data type: %x = [%s]", value, S7CommDataMap[i].name);
                break;
            }
        }
        break;

    case S7COMM_SUB_FUNC_MAP:
        for (int32 i = 0; i < GET_MAP_LEN(S7CommSubFuncMap); i++ ) {
            if ( (value == S7CommSubFuncMap[i].funcgrp) \
                 && (sub_value == S7CommSubFuncMap[i].subfunc )) {
                ret = i;
                PRINT_DBG_HEAD;
                print_dbg("S7 subfucntion: %x = [%s]", sub_value, S7CommSubFuncMap[i].subfuncname);
                break;
            }
        }
        break;

    default:    //default
        PRINT_ERR_HEAD;
        print_err("S7 Default pdu Map:value = %x,map_type = %d", value, map_type );
        break;
    }

    PRINT_DBG_HEAD;
    print_dbg("S7 FuncNameMap value=%x,ret = %d", value, ret);

    return ret;
}

/**
 * [CS7COMM::dofilter_src 分析源端数据报文]
 * @param  data   [数据包]
 * @param  size   [数据大小]
 * @param  rule   [规则信息]
 * @param  filter [黑白名单]
 * @param  msgout [返回信息，可为NULL]
 * @return        [PROTO_ACCEPT通过]
 */
int32 CS7COMM::dofilter_src(const pchar data, int32 size, const pchar rule, int32 filter, pchar msgout)
{
    if (m_initialized == INIT_OK) {

        PRINT_DBG_HEAD;
        print_dbg("S7 %d = (%d)%s", size, filter, rule);
        return PROTO_ACCEPT;
    }

    PRINT_DBG_HEAD;
    print_dbg("S7 NULL %d = (%d)%s", size, filter, rule);
    return PROTO_ACCEPT;
}

/**
 * [CS7COMM::dofilter_dst 分析目的端数据报文]
 * @param  data   [数据包]
 * @param  size   [数据大小]
 * @param  rule   [规则信息]
 * @param  filter [黑白名单]
 * @param  msgout [返回信息，可为NULL]
 * @return        [PROTO_ACCEPT通过]
 */
int32 CS7COMM::dofilter_dst(const pchar data, int32 size, const pchar rule, int32 filter, pchar msgout)
{
    if (m_initialized == INIT_OK) {

        PRINT_DBG_HEAD;
        print_dbg("S7 %d = (%d)%s", size, filter, rule);

        int32 ret = PROTO_ACCEPT;   //默认值
        S7COMMRULE s7comm_rule;
        uint32 offset = 0;

        if (filter == RULE_DIS) {   //当前规则无效
            PRINT_ERR_HEAD;
            print_err("S7 RULE INVALID %d = (%d)%s", /*size - */offset, filter, rule);
            return PROTO_ACCEPT;
        }

        if (PROTO_OK != (GetRule(rule, filter))) {
            PRINT_ERR_HEAD;
            print_err("S7 RULE Parse error %d = (%d)%s", /*size - */offset, filter, rule);
            return PROTO_ACCEPT;
        }

        //端口102. tcp包有内容，则TPKT+COTP检查
        offset = CheckTpktCotp( (uint8 *)data, size );
        if ( !offset ) {
            PRINT_DBG_HEAD;
            print_dbg("S7COMM DATA INVALID S7 %d = (%d)", size, filter);
            ret = ACCEPT_FILTER(filter);
            return ret;
        }

        if (0 == size - offset) {
            //报文内容只到cotp包，没有s7comm数据部分， check成功
            ret = PROTO_ACCEPT;
            PRINT_DBG_HEAD;
            print_dbg("recv s7comm package, protocol id check success，ret = %d", ret);
            return ret; 
        }

        // 有s7comm数据部分,check protocol id;
        S7COMMSTATE tmp_state;
        memset(&tmp_state, 0, sizeof(tmp_state));
        memcpy(&tmp_state, data + offset, MIN(sizeof(tmp_state), size - offset));

        ret = CheckS7Header(data,size,offset,&tmp_state);
        if ((PROTO_UNKNOWN == ret) || (PROTO_ERROR == ret)) {
            return ret;
        }

        ret = GetCmd(&tmp_state, &s7comm_rule);
        if ( (PROTO_OK == ret) ) {
            PRINT_DBG_HEAD;
            print_dbg("S7  ret = %d,rosctr:%x", ret, s7comm_rule.rosctr);

            ret = FilterData( &s7comm_rule, size - offset, msgout);
        } else {
            PRINT_ERR_HEAD;
            print_err("S7 NOCMD %d = (%d)%s", size, filter, rule);
#if __DEBUG_MORE__
            PRINT_ERR_HEAD;
            print_err("S7 NOCMD %d = (%d)%s, data = %s", size, filter, rule, printbuf(data, size));
#endif
        }

        PRINT_DBG_HEAD;
        print_dbg("S7 %d = (%d)%s,ret = %d", size, filter, rule, ret);

        return ret;
    }

    return PROTO_ACCEPT;
}

/**
 * [CS7COMM::CheckS7JobPdu S7 Job类型pdu校验]
 * @param  job_data [源数据]
 * @param  index    [索引值]
 * @return          [PROTO_OK成功]
 */
int32 CS7COMM::CheckS7JobPdu( PS7COMMSTATE job_data, pint32 index )
{
    int32 ret = PROTO_OK;
    int32 tmp_index = 0;

    PRINT_DBG_HEAD;
    print_dbg("S7 job pdu func type:%x", job_data->PARM.JOB.job_func);

    tmp_index = S7FuncNameMap( job_data->PARM.JOB.job_func, 0, S7COMM_FUNC_TYPE_MAP);
    if ( 0 > tmp_index ) {
        PRINT_ERR_HEAD;
        print_err("S7 job pdu func type=%x error,index=%d", job_data->PARM.JOB.job_func, tmp_index);
        ret = PROTO_ERROR;
    }
    *index = tmp_index;

    PRINT_DBG_HEAD;
    print_dbg("S7 CheckS7JobPdu ret=%d,index=%d", ret, tmp_index);

    return ret;
}

/**
 * [CS7COMM::CheckS7AckPdu S7 Ack类型pdu校验]
 * @param  ack_data [源数据]
 * @param  index    [索引值]
 * @return          [PROTO_OK成功]
 */
int32 CS7COMM::CheckS7AckPdu( PS7COMMSTATE ack_data, pint32 index )//index
{
    int32 ret   = PROTO_OK;
    int32 tmp_index = 0;

    PRINT_DBG_HEAD;
    print_dbg("S7 ack pdu func type=%x", ack_data->PARM.ACK.ack_func);

    tmp_index = S7FuncNameMap( ack_data->PARM.ACK.ack_func, 0, S7COMM_FUNC_TYPE_MAP);
    if ( 0 > tmp_index ) {
        PRINT_ERR_HEAD;
        print_err("S7 ack pdu func type=%x error,index=%d", ack_data->PARM.ACK.ack_func, tmp_index);
        ret = PROTO_ERROR;
    }
    *index = tmp_index;

    PRINT_DBG_HEAD;
    print_dbg("S7 CheckS7AckPdu ret=%d,index=%d", ret, tmp_index);

    return ret;
}

/**
 * [CS7COMM::CheckS7UserDataPdu S7 user_data类型pdu校验]
 * @param  user_data [源数据]
 * @param  index     [索引值]
 * @param  sub_index [子索引值]
 * @return           [PROTO_OK成功]
 */
int32 CS7COMM::CheckS7UserDataPdu( PS7COMMSTATE user_data, pint32 index, pint32 sub_index)
{
    int32 ret = PROTO_OK;
    int32 tmp_index = 0;
    int32 tmp_subindex = 0;

    PRINT_DBG_HEAD;
    print_dbg("S7 user_data pdu type=%x,sub_function=%x",
              user_data->PARM.UD.type, user_data->PARM.UD.sub_func);

    tmp_index = S7UDPduMap( user_data, S7COMM_DATA_TYPE_MAP );
    if ( 0 > tmp_index ) {
        PRINT_ERR_HEAD;
        print_err("S7 user_data pdu type=%x error,index=%d", \
                  GET_UD_TYPE(user_data->PARM.UD.type, UD_TYPE_FLAG), tmp_index);
        ret = PROTO_ERROR;
    }

    if (PROTO_OK == ret) {
        tmp_subindex = S7UDPduMap( user_data, S7COMM_FUNC_GRP_MAP );
        if ( 0 > tmp_subindex ) {
            PRINT_ERR_HEAD;
            print_err("S7 user_data pdu function_group=%x error,index=%d", \
                      GET_UD_TYPE(user_data->PARM.UD.type, UD_FUNCGRP_FLAG), tmp_subindex);
            ret = PROTO_ERROR;
        }
    }

    if (PROTO_OK == ret) {
        tmp_subindex = S7UDPduMap( user_data, S7COMM_SUB_FUNC_MAP );
        if ( 0 > tmp_subindex ) {
            PRINT_ERR_HEAD;
            print_err("S7 user_data pdu subfunction=%x error,index=%d", \
                      user_data->PARM.UD.sub_func, tmp_subindex);
            ret = PROTO_ERROR;
        }
    }
    *index = tmp_index;
    *sub_index = tmp_subindex;

    PRINT_DBG_HEAD;
    print_dbg("S7 CheckS7UserDataPdu ret=%d,index=%d", ret, tmp_index);

    return ret;
}

/**
 * [CS7COMM::S7RuleParamCheck rule规则匹配]
 * @param  list        [用户下发的匹配rule]
 * @param  map_type    [类型]
 * @param  s7comm_rule [s7comm匹配规则]
 * @return             [-1失败]
 */
int32 CS7COMM::S7RuleParamCheck(std::vector<std::string> &list, uint8 map_type, PS7COMMRULE s7comm_rule)
{
    uint8 index = 0;
    int32 ret = -1;

    PRINT_DBG_HEAD;
    print_dbg("S7 rule check map_type:=%d,index=%d", map_type, index);

    switch (map_type) {
    case S7COMM_FUNC_RULE_MAP:
        index = s7comm_rule->index;
        ret = findruleparam(list, S7CommFuncMap[ index ].name);//匹配数字，优化
        break;

    case S7COMM_TYPE_RULE_MAP:
        index = s7comm_rule->index;
        ret = findruleparam(list, S7CommDataMap[ index ].name);
        break;

    case S7COMM_FUNCGRP_RULE_MAP:
        index = s7comm_rule->sub_index;
        ret = findruleparam(list, S7CommSubFuncMap[ index ].funcgrpname);
        break;

    case S7COMM_SUBFUNC_RULE_MAP:
        index = s7comm_rule->sub_index;
        ret = findruleparam(list, S7CommSubFuncMap[ index ].subfuncname);
        break;

    default:
        PRINT_ERR_HEAD;
        print_err("S7 rule check error,map_type=%d,index=%d", map_type, s7comm_rule->index);
        break;
    }

    PRINT_DBG_HEAD;
    print_dbg("S7 S7RuleParamCheck ret=%d", ret);

    return ret;
}

/**
 * [CS7COMM::CopyPduToRule 将s7comm_state里面的数值拷贝到s7comm_rule中]
 * @param s7comm_state [源数据]
 * @param s7comm_rule  [s7comm匹配规则]
 * @param index        [索引值]
 * @param sub_index    [子索引值]
 */
void CS7COMM::CopyPduToRule(PS7COMMSTATE s7comm_state, PS7COMMRULE s7comm_rule, int32 index, int32 sub_index)
{
    switch (s7comm_state->rosctr) {
    case S7COMM_ROSCTR_JOB: //job
        s7comm_rule->rosctr = s7comm_state->rosctr;
        s7comm_rule->func   = s7comm_state->PARM.JOB.job_func;
        s7comm_rule->index  = index;

        PRINT_DBG_HEAD;
        print_dbg("S7 job rule,function:%x=[%s]", \
                  s7comm_rule->func, S7CommFuncMap[index].name);
        break;

    case S7COMM_ROSCTR_ACK://ack和ack_data
    case S7COMM_ROSCTR_ACK_DATA:
        s7comm_rule->rosctr = s7comm_state->rosctr;
        s7comm_rule->func   = s7comm_state->PARM.ACK.ack_func;
        s7comm_rule->index  = index;

        PRINT_DBG_HEAD;
        print_dbg("S7 ack rule,function:%x=[%s]", \
                  s7comm_rule->func, S7CommFuncMap[index].name);
        break;

    case S7COMM_ROSCTR_USERDATA:  //user_data
        s7comm_rule->rosctr   = s7comm_state->rosctr;
        s7comm_rule->type     = GET_UD_TYPE(s7comm_state->PARM.UD.type, UD_TYPE_FLAG);
        s7comm_rule->func_grp = GET_UD_TYPE(s7comm_state->PARM.UD.type, UD_FUNCGRP_FLAG);
        s7comm_rule->sub_func = s7comm_state->PARM.UD.sub_func;
        s7comm_rule->index    = index;
        s7comm_rule->sub_index = sub_index;

        PRINT_DBG_HEAD;
        print_dbg("S7 user_data rule, type:%x=[%s],function group:%x=[%s],sub_function:%x=[%s]", \
                  s7comm_rule->type, S7CommDataMap[index].name,  \
                  s7comm_rule->func_grp, S7CommSubFuncMap[sub_index].funcgrpname,   \
                  s7comm_rule->sub_func, S7CommSubFuncMap[sub_index].subfuncname);
        break;

    default:
        PRINT_ERR_HEAD;
        print_err("S7 header rosctr=%x unknow!", s7comm_state->rosctr);
        break;
    }
}

/**
 * [CS7COMM::CheckS7DataEmpty 检查data_len=0时的数据]
 * @param  s7comm_state [数据源]
 * @param  s7comm_rule  [s7comm匹配规则]
 * @return              [PROTO_OK成功]
 */
int32 CS7COMM::CheckS7DataEmpty(PS7COMMSTATE s7comm_state, PS7COMMRULE s7comm_rule)
{
    int32 ret   = PROTO_OK;
    int32 index = 0;
    int32 sub_index = 0;

    PRINT_DBG_HEAD;
    print_dbg("S7 header rosctr=%x", s7comm_state->rosctr);

    switch (s7comm_state->rosctr) {
    case S7COMM_ROSCTR_JOB:  //job
        ret = CheckS7JobPdu(s7comm_state, &index);//*index

        if ( PROTO_OK == ret ) {
            CopyPduToRule(s7comm_state, s7comm_rule, index, 0);
        }
        break;

    case S7COMM_ROSCTR_ACK://ack和ack_data
    case S7COMM_ROSCTR_ACK_DATA:
        ret = CheckS7AckPdu(s7comm_state, &index);
        if ( PROTO_OK == ret ) {
            CopyPduToRule(s7comm_state, s7comm_rule, index, 0);
        }
        break;

    case S7COMM_ROSCTR_USERDATA:  //user_data
        ret = CheckS7UserDataPdu(s7comm_state, &index, &sub_index);
        if ( PROTO_OK == ret ) {
            CopyPduToRule(s7comm_state, s7comm_rule, index, sub_index);
        }
        break;

    default:
        ret = PROTO_ERROR;
        PRINT_ERR_HEAD;
        print_err("S7 header rosctr=%x unknow!", s7comm_state->rosctr);
        break;
    }

    PRINT_DBG_HEAD;
    print_dbg("S7 CheckS7DataEmpty ret=%d", ret);

    return ret;
}

/**
 * [CS7COMM::CheckFunctype 检查job和ack的functype]
 * @param  functype [待检查值]
 * @return          [PROTO_OK成功]
 */
int32 CS7COMM::CheckFunctype(uint8 functype)
{
    int32 ret = -1;

    if ( (S7COMM_SERV_READVAR == functype) || (S7COMM_SERV_WRITEVAR == functype) \
         || (S7COMM_FUNC_DOWNLOADBLOCK == functype) || (S7COMM_FUNC_UPLOAD == functype) ) {
        ret = PROTO_OK;
    }
    return ret;
}

/**
 * [CS7COMM::CheckS7DataExist 检查data_len>0时的数据]
 * @param  s7comm_state [数据源]
 * @param  s7comm_rule  [s7comm匹配规则]
 * @return              [PROTO_OK成功]
 */
int32 CS7COMM::CheckS7DataExist(PS7COMMSTATE s7comm_state, PS7COMMRULE s7comm_rule)
{

    uint8 functype = 0;

    uint32 ret  = PROTO_OK;
    int32 index = 0;
    int32 sub_index = 0;

    PRINT_DBG_HEAD;
    print_dbg("S7 header rosctr=%x", s7comm_state->rosctr);

    switch (s7comm_state->rosctr) {
    case S7COMM_ROSCTR_JOB:
        functype = s7comm_state->PARM.JOB.job_func;
        break;

    case S7COMM_ROSCTR_ACK:
    case S7COMM_ROSCTR_ACK_DATA:
        functype = s7comm_state->PARM.ACK.ack_func;
        break;

    case S7COMM_ROSCTR_USERDATA:
        break;

    default:
        ret = PROTO_ERROR;
        PRINT_ERR_HEAD;
        print_err("S7 header rosctr=%x unknown!", s7comm_state->rosctr);
        break;
    }
    //函数ret = CheckFuncType()
    ret = CheckFunctype(functype);
    if (PROTO_OK == ret) {

        switch (s7comm_state->rosctr) {
        case S7COMM_ROSCTR_JOB:  //job
            ret = CheckS7JobPdu(s7comm_state, &index);
            if ( PROTO_OK == ret ) {
                CopyPduToRule(s7comm_state, s7comm_rule, index, 0);
            }
            break;

        case S7COMM_ROSCTR_ACK://ack和ack_data
        case S7COMM_ROSCTR_ACK_DATA:
            ret = CheckS7AckPdu(s7comm_state, &index);
            if ( PROTO_OK == ret ) {
                CopyPduToRule(s7comm_state, s7comm_rule, index, 0);
            }
            break;

        default:
            ret = PROTO_ERROR;
            PRINT_ERR_HEAD;
            print_err("S7 header rosctr=%x unknown!", s7comm_state->rosctr);
            break;

        }
    } else { //user data
        ret = CheckS7UserDataPdu(s7comm_state, &index, &sub_index);
        if ( PROTO_OK == ret ) {
            CopyPduToRule(s7comm_state, s7comm_rule, index, sub_index);
        }
    }

    PRINT_DBG_HEAD;
    print_dbg("S7 CheckS7DataExist ret=%d", ret);

    return ret;
}

/**
 * [CheckS7Header 检查s7协议header,替代S7HeaderCheck]
 * @param  s7comm_state [源数据]
 * @return              [PROTO_OK成功]
 */
int32 CS7COMM::CheckS7Header(const pchar data, int32 size, uint32 offset, PS7COMMSTATE s7comm_state)
{
    int32 ret = PROTO_OK;
    int32 rosctr_ret = PROTO_OK;

    PRINT_DBG_HEAD;
    print_dbg("S7 protocol_id=%x,rosctr=%x", s7comm_state->protocol_id, s7comm_state->rosctr);

    if (S7COMM_PROTOCOL_ID != s7comm_state->protocol_id) {
        PRINT_DBG_HEAD;
        print_dbg("recv non s7comm package, protocol_id = %d error!", s7comm_state->protocol_id);
        ret = PROTO_UNKNOWN;
    } else {
        //protocol id正确，继续匹配
        rosctr_ret = S7FuncNameMap(s7comm_state->rosctr, 0, S7COMM_PDU_TYPE_MAP);
        if ( 0 > rosctr_ret ) {
            PRINT_ERR_HEAD;
            print_err("S7 header rosctr=%x error!", s7comm_state->rosctr);
            ret = PROTO_ERROR;
        } else {
            //检查s7报文的长度是否与报文中描述的长度一致
            if ((size - offset) < (offsetof(S7COMMSTATE, PARM) + ntohs(s7comm_state->para_len) \
                                   + ntohs(s7comm_state->data_len) ) ) {
                PRINT_ERR_HEAD;
                print_err("recv size=[%d,%d] error,header_len=[%d],param_len=[%d],data_len=[%d],data=%s", \
                          size, offset, offsetof(S7COMMSTATE, PARM), ntohs(s7comm_state->para_len), \
                          ntohs(s7comm_state->para_len), printbuf(data, size));
                ret = PROTO_ERROR;
                //return ACCEPT_FILTER(rulebak.filter);
            }
        }
    }

    PRINT_DBG_HEAD;
    print_dbg("S7 CheckS7Header ret=%d", ret);

    return ret;
}

/**
 * [S7HeaderCheck 检查s7协议header]
 * @param  s7comm_state [源数据]
 * @return              [PROTO_OK成功]
 */
int32 CS7COMM::S7HeaderCheck(PS7COMMSTATE s7comm_state)
{
    int32 ret        = PROTO_OK;
    int32 rosctr_ret = 0;

    PRINT_DBG_HEAD;
    print_dbg("S7 protocol_id=%x,rosctr=%x", s7comm_state->protocol_id, s7comm_state->rosctr);

    if ( S7COMM_PROTOCOL_ID != s7comm_state->protocol_id ) {
        PRINT_ERR_HEAD;
        print_err("S7 protoocol_id=%x error!", s7comm_state->protocol_id);
        ret = PROTO_ERROR;
    } else {
        rosctr_ret = S7FuncNameMap(s7comm_state->rosctr, 0, S7COMM_PDU_TYPE_MAP);
        if ( 0 > rosctr_ret ) {
            PRINT_ERR_HEAD;
            print_err("S7 header rosctr=%x error!", s7comm_state->rosctr);
            ret = PROTO_ERROR;
        }
    }

    PRINT_DBG_HEAD;
    print_dbg("S7 S7HeaderCheck ret=%d", ret);

    return ret;
}

/**
 * [CS7COMM::GetCmd 解析S7协议]
 * @param  s7comm_state        [s7comm包的header+param数据]
 * @param  s7comm_rule [s7comm匹配规则]
 * @return             [PROTO_OK成功]
 */
int32 CS7COMM::GetCmd(PS7COMMSTATE s7comm_state, PS7COMMRULE s7comm_rule)
{
    int32 ret = PROTO_OK;

    //S7COMMSTATE s7comm_state;
    //memset(&s7comm_state, 0, sizeof(s7comm_state));
    //memcpy(&s7comm_state, data, sizeof(s7comm_state));

    //PRINT_DBG_HEAD;
    //print_dbg("S7 header protocol_id=%x,rosctr=%x", s7comm_state.protocol_id, s7comm_state.rosctr);
    //s7 header校验
    //ret = S7HeaderCheck(&s7comm_state);
    //if ( PROTO_OK == ret ) {
        if ( 0 == s7comm_state->data_len ) {     //data_len =0
            ret = CheckS7DataEmpty(s7comm_state, s7comm_rule);
        } else {    //data_len > 0
            ret = CheckS7DataExist(s7comm_state, s7comm_rule);
        }
    //}

    PRINT_DBG_HEAD;
    print_dbg("S7 GetCmd ret=%d", ret);

    return ret;

}

/**
 * [CS7COMM::FilterUserData 匹配User_data规则]
 * @param  s7comm_rule [s7报文rule]
 * @param  msgout      [输出信息]
 * @return             [PROTO_ACCEPT通过]
 */
int32 CS7COMM::FilterUserData(PS7COMMRULE s7comm_rule, pchar msgout )
{
    int32   index     = 0;
    int32   sub_index = 0;
    int32   ret       = PROTO_ACCEPT;

    index = s7comm_rule->index;//index
    sub_index = s7comm_rule->sub_index;

    if ( -1 == S7RuleParamCheck(cmdlist, S7COMM_TYPE_RULE_MAP, s7comm_rule) ) {
        PRINT_DBG_HEAD;
        print_dbg("S7 FilterUserData ret = %d,msgout= %s", ret, msgout);
        ret = ACCEPT_FILTER(rulebak.filter);
    } else {
        PRINT_DBG_HEAD;
        print_dbg("S7 FilterUserData ret = %d,msgout= %s", ret, msgout);
        ret = DROP_FILTER(rulebak.filter);
    }

    PRINT_DBG_HEAD;
    print_dbg("S7 FilterUserData ret = %d,msgout= %s", ret, msgout);
    if (is_rulemsgout(ret, msgout) || (S7CommDataMap[ index ].rw == PROTO_WRITE)) {
        sprintf(msgout, "%s%s,%s]", MLOGHEAD(S7COMM), "USER DATA Type", S7CommDataMap[ index ].name);
    }

    if (PROTO_ACCEPT == ret ) {
        if ( (-1 == S7RuleParamCheck(funcgrplist, S7COMM_FUNCGRP_RULE_MAP, s7comm_rule))
             && ( -1 == S7RuleParamCheck(subfunclist, S7COMM_SUBFUNC_RULE_MAP, s7comm_rule)) ) {
            ret = ACCEPT_FILTER(rulebak.filter);
        } else {
            ret = DROP_FILTER(rulebak.filter);
        }

        if (is_rulemsgout(ret, msgout) || (S7CommSubFuncMap[ sub_index ].rw == PROTO_WRITE)) {
            sprintf(msgout, "%s%s,type=%s,func_group=%s,subfunc=%s]", MLOGHEAD(S7COMM), "USER DATA Type", \
                    S7CommDataMap[ index ].name, S7CommSubFuncMap[ sub_index ].funcgrpname, \
                    S7CommSubFuncMap[ sub_index ].subfuncname);
        }
    }

    PRINT_DBG_HEAD;
    print_dbg("S7 FilterUserData ret = %d,msgout= %s", ret, msgout);

    return ret;
}

/**
 * [CS7COMM::FilterData 进行规则过滤]
 * @param  s7comm_rule [s7报文rule]
 * @param  size        [数据大小]
 * @param  msgout      [输出信息]
 * @return             [PROTO_ACCEPT通过]
 */
int32 CS7COMM::FilterData( PS7COMMRULE s7comm_rule, int32 size, pchar msgout)
{
    uint8 index = 0;
    int32 ret   = PROTO_ACCEPT;

    PRINT_DBG_HEAD;
    print_dbg("S7 PDU size = %d,rosctr:%x", size, s7comm_rule->rosctr);

    index = s7comm_rule->index;

    switch (s7comm_rule->rosctr) {
    case S7COMM_ROSCTR_JOB:    //job
        if ( -1 == S7RuleParamCheck(cmdlist, S7COMM_FUNC_RULE_MAP, s7comm_rule) ) {
            ret = ACCEPT_FILTER(rulebak.filter);
        } else {
            ret = DROP_FILTER(rulebak.filter);
        }

        if (is_rulemsgout(ret, msgout) || (S7CommFuncMap[ index ].rw == PROTO_WRITE)) {
            sprintf(msgout, "%s%s,%s]", MLOGHEAD(S7COMM), "JOB Type",
                    S7CommFuncMap[ index ].name);
        }
        break;

    case S7COMM_ROSCTR_ACK:     //ack+ack_data
    case S7COMM_ROSCTR_ACK_DATA:
        if ( -1 == S7RuleParamCheck(cmdlist, S7COMM_FUNC_RULE_MAP, s7comm_rule) ) {
            ret = ACCEPT_FILTER(rulebak.filter);
        } else {
            ret = DROP_FILTER(rulebak.filter);
        }

        if (is_rulemsgout(ret, msgout) || (S7CommFuncMap[ index ].rw == PROTO_WRITE)) {
            sprintf(msgout, "%s%s,%s]", MLOGHEAD(S7COMM), "ACK Type",
                    S7CommFuncMap[ index ].name);
        }
        break;

    case S7COMM_ROSCTR_USERDATA:  //user_data
        ret = FilterUserData(s7comm_rule, msgout);
        break;

    default:
        PRINT_ERR_HEAD;
        print_err("S7 header type unknown, %x!", s7comm_rule->rosctr);
        //设置ret值
        break;
    }

    PRINT_DBG_HEAD;
    print_dbg("S7 FilterData size = %d,msgout = %s,ret = %d", size, msgout, ret);

    return ret;

}

/**
 * [CS7COMM::GetRule 解析用户规则]
 * @param  rule   [数据指针]
 * @param  filter [黑白名单]
 * @return        [PROTO_OK成功]
 */
int32 CS7COMM::GetRule(const pchar rule, int32 filter)
{
    bool bcmd = false;

    if (is_strempty(rule))
        return PROTO_ERROR;

    PRINT_DBG_HEAD;
    print_dbg("S7 RULE %d = %s", filter, rule);

    if ((bcmd = (strcasecmp(rulebak.rule, rule) != 0)) || (filter != rulebak.filter)) {

        rulebak.filter = filter;

        if (bcmd) {

            PRINT_DBG_HEAD;
            print_dbg("S7 RULE UPDATE %d = %s", filter, rule);

            bool berr = true;

            //获取过滤规则
            berr &= (getruleparam(cmdlist, rule, "CMD=[") == 0);
            berr &= (getruleparam(funcgrplist, rule, "FUNCGRP=[") == 0);
            berr &= (getruleparam(subfunclist, rule, "SUBFUNC=[") == 0);

            //更新记录
            memset(rulebak.rule, 0, sizeof(rulebak.rule));
            PRINT_DBG_HEAD;
            print_dbg("S7 berr:%d ", berr);
            //记录为cmd[*]时不应返回error
            if (berr) {
                PRINT_ERR_HEAD;
                print_err("S7 RULE %d = %s", filter, rule);

                //return PROTO_ERROR;
            }

            strncpy(rulebak.rule, rule, sizeof(rulebak.rule) - 1);
        }
    }

    PRINT_DBG_HEAD;
    print_dbg("S7 RULE %d = %s", filter, rule);

    return PROTO_OK;
}
/*******************************************************************************************
*
*                                   动态链接库调用定义
*
*******************************************************************************************/

static CS7COMM self;   //全局

/**
 * [IMInitialize 模块注册验证，检查系统配置是否正常]
 * @param  verdict [系统路径]
 * @return         [PROTO_ERROR or PROTO_OK]
 */
int32 IMInitialize(const pchar verdict)
{
    if (is_strempty(verdict)) return PROTO_ERROR;

    return self.initialize(verdict);
}

/**
 * [IMVersion 模块版本]
 * @param  version [版本信息(模块名+版本)，不超过48Bytes]
 * @return         [PROTO_ERROR or PROTO_OK]
 */
int32 IMVersion(pchar version)
{
    if (version == NULL) return PROTO_ERROR;
    return self.version(version);
}


/**
 * [IMDatasrc IMDatadst 协议数据报文处理]
 * @param  data   [数据包]
 * @param  size   [数据大小]
 * @param  rule   [规则信息]
 * @param  filter [黑白名单]
 * @param  msgout [返回信息，可为NULL]
 * @return        [PROTO_ERROR错误，否则有效]
 */
int32 IMDatasrc(const pchar data, int32 size, const pchar rule, int32 filter, pchar msgout, datacall callfunc)
{
    if ((data == NULL) || (size < 0)) {
        if (msgout != NULL) sprintf(msgout, "%s]", MLOGHEAD(S7COMM));
        return PROTO_ERROR;
    }
    if (is_strempty(rule) || (filter > RULE_BLACK) || (size == 0)) return PROTO_ACCEPT;

    return self.dofilter_src(data, size, rule, filter, msgout);
}

int32 IMDatadst(const pchar data, int32 size, const pchar rule, int32 filter, pchar msgout, datacall callfunc)
{
    if ((data == NULL) || (size < 0)) {
        if (msgout != NULL) sprintf(msgout, "%s]", MLOGHEAD(S7COMM));
        return PROTO_ERROR;
    }
    if (is_strempty(rule) || (filter > RULE_BLACK) || (size == 0)) return PROTO_ACCEPT;

    return self.dofilter_dst(data, size, rule, filter, msgout);
}

/**
 * [IMDataBoth 双向相关协议数据报文处理]
 * @param  dataaddr [数据通信链路地址]
 * @param  data     [数据包]
 * @param  size     [数据大小]
 * @param  rule     [规则信息]
 * @param  filter   [黑白名单]
 * @param  msgout   [返回信息，可为NULL]
 * @return          [PROTO_ERROR错误，否则有效]
 */

int32 IMDataBoth(DATANETADDR dataaddr, const pchar data, int32 size, const pchar rule, int32 filter, pchar msgout, datacall callfunc)
{
    return PROTO_UNKNOWN_IF;
}

/**
 * [IMRelease 回收释放资源]
 * @param  dataaddr [数据链路地址，NULL表示全部资源]
 * @param  callfunc [外部回调函数，可为NULL]
 * @return          [PROTO_OK成功]
 */
int32 IMRelease(PDATANETADDR dataaddr, datacall callfunc)
{
    return PROTO_OK;
}
